#include "Ghost.h"

#include <ctime>
#include<iostream>
using namespace std;

const float white[] = {1.0,1.0,1.0,1.0};
const float black[] = {0.0,0.0,0.0,1.0};

const float turquoise[] =  {0.0,1.0,0.87,1.0};
const float red      [] =  {1.0,0.0,0.0,1.0};
const float orange   [] =  {1.0,0.72,0.28,1.0};
const float pink     [] =  {1.0,0.72,0.87,1.0};

const float blue_slow[] = {0.05,0.29,0.73,1.0};

	   static GLubyte im_ghost[3*iw_ghost*ih_ghost];
   static float f_ghost = 10.0F ;

Ghost::Ghost(float x, float z,float angle, float width, Field *field, Pacman* pacman,int number) : Character(x,z,angle,width,field)
{
	this->number = number;
	startCounter = (double)clock()/CLOCKS_PER_SEC;
	field->getGrid()->at(getAbsoluteX())->at(getAbsoluteZ()) = GHOST;
	speed= 0.1;
	distanceFromPacman=50;//une grande valeur pour rester tranquille lors de la premiere frame
	this->pacman = pacman;
	turnMode = false;
	comportement = NORMAL;

}

Ghost::~Ghost(void)
{
}

void Ghost::init(){
      lectureTexture(tf_ghost,iw_ghost,ih_ghost) ;
glBindTexture(GL_TEXTURE_2D, texName[1]);
  glTexParameterf(GL_TEXTURE_2D,
                  GL_TEXTURE_WRAP_S,
                  GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D,
                  GL_TEXTURE_WRAP_T,
                  GL_REPEAT);
  glTexParameterf(GL_TEXTURE_2D,
                  GL_TEXTURE_MAG_FILTER,
                  GL_NEAREST);
  glTexParameterf(GL_TEXTURE_2D,
                  GL_TEXTURE_MIN_FILTER,
                  GL_NEAREST);
  glTexImage2D(GL_TEXTURE_2D,
               0,3,iw_ghost,ih_ghost,0,
               GL_RGB,
               GL_UNSIGNED_BYTE,
               im_ghost);

   glNewList(GHOST_ALIVE,GL_COMPILE);

      float eyes_distance = 0.4;

         glutSolidSphere(0.5,30,30);

         glRotatef(90,1,0,0);

         GLUquadric * quadric = gluNewQuadric();
         gluCylinder(quadric,0.5,0.5,0.5,30,30);

         // oeil droit
         glPushMatrix();
            glColor3fv(white);

            glTranslatef(-0.1,eyes_distance,-0.1);
            glScalef(1,1,1.5);
            glutSolidSphere(0.1,30,30);

            // iris
            glTranslatef(0,0.1,0);
            glColor3fv(black);
            glutSolidSphere(0.05,30,30);

         glPopMatrix();

         // oeil gauche
         glPushMatrix();
            glColor3fv(white);
            glTranslatef(0.1,eyes_distance,-0.1);
            glScalef(1,1,1.5);
            glutSolidSphere(0.1,30,30);

            // iris

            glTranslatef(0,0.1,0);
            glColor3fv(black);
            glutSolidSphere(0.05,30,30);
         glPopMatrix();



   glEndList();


   glNewList(GHOST_DEAD,GL_COMPILE);

         glRotatef(90,1,0,0);

         //oeil droit
         glPushMatrix();
            glColor3fv(white);

            glTranslatef(-0.1,eyes_distance,-0.1);
            glScalef(1,1,1.5);
            glutSolidSphere(0.1,30,30);

            // iris

            glTranslatef(0,0.1,0);
            glColor3fv(black);
            glutSolidSphere(0.05,30,30);

         glPopMatrix();

         // oeil gauche
         glPushMatrix();
            glColor3fv(white);
            glTranslatef(0.1,eyes_distance,-0.1);
            glScalef(1,1,1.5);
            glutSolidSphere(0.1,30,30);
            // iris

            glTranslatef(0,0.1,0);
            glColor3fv(black);
            glutSolidSphere(0.05,30,30);

         glPopMatrix();
   glEndList();

      glNewList(GHOST_SLOW,GL_COMPILE);

         glEnable(GL_TEXTURE_2D);
         glBindTexture(GL_TEXTURE_2D, texName[1]);
         glutSolidSphere(0.5,30,30);

         glRotatef(90,1,0,0);
         gluCylinder(quadric,0.5,0.5,0.5,30,30);
         gluQuadricTexture(quadric,true);
glColor3fv(white);
         // oeil droit
         glPushMatrix();
            glColor3fv(white);

            glTranslatef(-0.1,eyes_distance,-0.1);
            glScalef(1,1,1.5);
            glutSolidSphere(0.1,30,30);


         glPopMatrix();

         // oeil gauche
         glPushMatrix();
            glColor3fv(white);
            glTranslatef(0.1,eyes_distance,-0.1);
            glScalef(1,1,1.5);
            glutSolidSphere(0.1,30,30);

         glPopMatrix();
         glDisable(GL_TEXTURE_2D);
   glEndList();

}

void Ghost::lectureTexture(char *fichier, int dx,int dy) {
   printf("\n fichier : %s",fichier);

  FILE *f = fopen(fichier,"rb") ;
  if ( f ) {
    for ( int i = 0 ; i < dx ; i++ )
      for ( int j = 0 ; j < dy ; j++ )
        fread(&im_ghost[(j*dy+i)*3],1,3,f) ;
    fclose(f) ; }
}
void Ghost::display(){

	glPushMatrix();
	glTranslatef(positionX,0,positionZ);
	glRotatef(angle,0,1,0);

	glColor3f(1,1,1);
	if(comportement == DEAD)
		buildDeadGhost();
	else
		buildLivingGhost();

	glPopMatrix();
}

void Ghost :: action(){
	if((double)clock()/CLOCKS_PER_SEC - startCounter >= number)
	{
		adjustAngle();
		adjustPosition();

		if(isInTheMiddle() && !turnMode)
		{
			if(getAbsoluteX() == field->getSpawnX() && getAbsoluteZ() == field->getSpawnZ())
				comportement = NORMAL;

			if(comportement == DEAD)
			{
				speed= 0.1;
				int dir =  returnMode();
				if(dir!=NONE )
				{
					direction = dir;
				}
				else
				{
					resetRandomDirection();
					direction = randomMode();
				}
			}
			else if(field->isInSpawnArea(getRoundedX(),getRoundedZ()))
			{
				speed= 0.1;
				direction = getOutMode();

			}
			else if(comportement == NORMAL)
			{
				speed= 0.1;
				int dir = followMode();

				if(dir!=NONE )
				{
					direction = dir;
				}
				else
				{
					resetRandomDirection();
					direction = randomMode();
				}
			}
			else if(comportement == RUNAWAY)
			{
				speed= 0.01;
				resetRandomDirection();
				direction = randomMode();
			}
		}

		switch (direction)
		{
			case UP :
			{
				if(angle > 180.1 || angle < 179.9)
				{
					turnMode = true;
					if(angle > 180.1 )
						angle -= rotationSpeed;
					else
						angle += rotationSpeed;
				}
				else
				{
					turnMode = false;
					positionZ -= speed;
				}
			}
			break;
			case DOWN :
			{
				if( angle>0.1 )
				{
					turnMode = true;
					if(angle > 180)
						angle += rotationSpeed;
					else
						angle -= rotationSpeed;
				}
				else
				{
					positionZ += speed;
					turnMode = false;
				}
			}
			break;
			case LEFT :
			{
				if( angle > 270.1 || angle < 269.1 )
				{
					turnMode = true;
					if(angle > 270 || angle < 90)
						angle -= rotationSpeed;
					else
						angle += rotationSpeed;
				}
				else
				{
					turnMode = false;
					positionX -= speed;
				}
			}
			break;
			case RIGHT :
			{
				if( angle >90.1 || angle < 89.9 )
				{
					turnMode = true;
					if(angle > 270 || angle < 90)
						angle += rotationSpeed;
					else
						angle -= rotationSpeed;
				}
				else
				{
					turnMode = false;
					positionX += speed;
				}
			}
			break;
		}
	}
}

int Ghost :: getOutMode(){
	int X = getAbsoluteX() - field->getExitX();
	int Z = getAbsoluteZ() - field->getExitZ();
	int d1= NONE;
	int d2 = NONE;

	if(X>0 && direction!=RIGHT)
		d1 = LEFT;
	else if(X<0 && direction!=LEFT)
	{
		X=-X;
		d1=RIGHT;
	}

	if(Z>0 && direction!=DOWN)
		d2 = UP;
	else if(Z<0 && direction!=UP)
	{
		Z=-Z;
		d2 = DOWN;
	}

	if(d1 == NONE && isMovableWithoutTwisted(d2))
		return d2;
	else if(d2 == NONE && isMovableWithoutTwisted(d1))
		return d1;
	else if(  (X > Z) && isMovableWithoutTwisted(d2))
		return d2;
	else if(isMovableWithoutTwisted(d1))
		return d1;
	return NONE;
}

int Ghost :: returnMode(){

	cout<<"direction = "<<direction<<endl;
	//system("PAUSE");

	int X = getAbsoluteX() - field->getSpawnX();
	int Z = getAbsoluteZ() - field->getSpawnZ();
	int d1= NONE;
	int d2 = NONE;

	if(X>0 && direction!=RIGHT)
		d1 = LEFT;
	else if(X<0 && direction!=LEFT)
	{
		X=-X;
		d1=RIGHT;
	}

	if(Z>0 && direction!=DOWN)
		d2 = UP;
	else if(Z<0 && direction!=UP)
	{
		Z=-Z;
		d2 = DOWN;
	}

	if(d1 == NONE && isMovableWithoutTwisted(d2))
		return d2;
	else if(d2 == NONE && isMovableWithoutTwisted(d1))
		return d1;
	else if(  (X > Z) && isMovableWithoutTwisted(d2))
		return d2;
	else if(isMovableWithoutTwisted(d1))
		return d1;
	return NONE;
}

int Ghost::followMode(){
	if( distanceFromPacman<=15 )
	{
		int X = getAbsoluteX() - pacman->getAbsoluteX();
		int Z = getAbsoluteZ() - pacman->getAbsoluteZ();
		int d1= NONE;
		int d2 = NONE;

		if(X>0 && direction!=RIGHT)
			d1 = LEFT;
		else if(X<0 && direction!=LEFT)
		{
			X=-X;
			d1=RIGHT;
		}
		if(Z>0 && direction!=DOWN)
			d2 = UP;
		else if(Z<0 && direction!=UP)
		{
			Z=-Z;
			d2 = DOWN;
		}

		if(d1 == NONE && isMovable(d2))
			return d2;
		else if(d2 == NONE && isMovable(d1))
			return d1;
		else if(  (X > Z) && isMovable(d2))
			return d2;
		else if(isMovable(d1))
			return d1;
	}

	return NONE;
}


int Ghost::randomMode(){
	if(isMovable(next1))
		return next1;
	else if(isMovable(next2))
		return next2;
	else if(isMovable(next3))
		return next3;
	else
		return next4;
}

void Ghost :: resetRandomDirection(){
	vector<int> dir;
	dir.push_back(UP);
	dir.push_back(DOWN);
	dir.push_back(LEFT);
	dir.push_back(RIGHT);
	next1 = -1;
	next2 = -1;
	next3 = -1;
	next4 = -1;

	switch(direction){
	case UP:
		{
			dir.erase(dir.begin()+1);
		}
	break;
	case DOWN:
		{
			dir.erase(dir.begin());
		}
	break;
	case RIGHT:
		{
			dir.erase(dir.begin()+2);
		}
	break;
	case LEFT:
		{
			dir.erase(dir.begin()+3);
		}
	break;
	}

	while(dir.size() != 0){
		int random = rand()%dir.size();
		if(next1 == -1)
			next1 = dir.at(random);
		if(next2 == -1)
			next2 = dir.at(random);
		else if(next3 == -1)
			next3 = dir.at(random);
		else
			next4 = dir.at(random);

		dir.erase(dir.begin()+random);
	}

}

float Ghost ::getDistanceFromPacman(){
	return distanceFromPacman;
}

void Ghost::setDistanceFromPacman(float d){
	distanceFromPacman = d;
}

int Ghost :: getComportement(){
	return comportement;
}

void Ghost :: setComportement(int c){
	comportement = c;
}

void Ghost :: buildLivingGhost(){  //TODO
   if(comportement != RUNAWAY){
      switch(number){
         case 0:
         glColor3fv(turquoise);
         break;
         case 1:
         glColor3fv(red);
         break;
         case 2:
         glColor3fv(orange);
         break;
         case 3:
         glColor3fv(pink);
         break;
         default:
         glColor3fv(turquoise);
      }
   glCallList(GHOST_ALIVE);
}else{
      glCallList(GHOST_SLOW);
}
}

void Ghost:: buildDeadGhost(){//TODO
   glColor3fv(blue_slow);
   glCallList(GHOST_DEAD);

}
